-- 解读瓦器人代码,折磨开始啦
local assets =
{
    Asset("ANIM", "anim/sw_pigbot.zip"),
    Asset("ANIM", "anim/firefighter_placement.zip"),

    Asset("SCRIPT", "scripts/prefabs/sw_pigbot_common.lua"),
}

---------------------------------------------------------------------------------------------------

local brain = require "brains/sw_pigbotbrain"
local SwPigbotCommon = require "prefabs/sw_pigbot_common" -- 猪机人通用函数

local VISUAL_SCALE = 1.05

---------------------------------------------------------------------------------------------------

local CIRCLE_RADIUS_SCALE = 1888 / 150 / 2 -- 原美术尺寸 / 动画比例 / 2 (减半得半径).

-- 创建范围圈实体的函数
local function CreateHelperRadiusCircle()
    local inst = CreateEntity()

    -- [[不联网实体]]
    inst.entity:SetCanSleep(false)
    inst.persists = false

    inst.entity:AddTransform()
    inst.entity:AddAnimState()

    inst:AddTag("CLASSIFIED")
    inst:AddTag("NOCLICK")
    inst:AddTag("placer")

    inst.AnimState:SetBank("firefighter_placement")
    inst.AnimState:SetBuild("firefighter_placement")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetAddColour(0, .2, .5, 0)
    inst.AnimState:SetLightOverride(1)
    inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
    inst.AnimState:SetLayer(LAYER_WORLD_BACKGROUND)
    inst.AnimState:SetSortOrder(3)

    -- CIRCLE_RADIUS_SCALE 就用官方的参数,我们只需要更改前面的范围,原版瓦器人半径是15
    local scale = TUNING.SHADOWWOVEN.SW_PIGBOT_WORK_RADIUS / CIRCLE_RADIUS_SCALE -- 设定缩放比例:15 / (1888 / 150 / 2)

    inst.AnimState:SetScale(scale, scale)

    return inst
end


local function OnOriginDirty(inst)
    if inst.helper ~= nil then
        inst.helper.Transform:SetPosition(inst._originx:value(), 0, inst._originz:value())
    end
end

local function OnEnableHelper(inst, enabled, recipename, placerinst)
    if enabled and recipename ~= nil and not inst:HasTag("broken") and inst:GetCurrentPlatform() == nil then
        if inst.helper == nil then
            inst.helper = CreateHelperRadiusCircle()  -- 创建圈实体

            OnOriginDirty(inst)                       -- 设置圈实体的坐标

            inst.AnimState:SetAddColour(0, .2, .5, 0) -- 设置圈实体颜色
        end
    elseif inst.helper ~= nil then
        inst.AnimState:SetAddColour(0, 0, 0, 0)

        inst.helper:Remove()
        inst.helper = nil
    end
end

local function OnStartHelper(inst)
    -- 如果被捡走了,不会显示范围
    if inst.replica.inventoryitem ~= nil and inst.replica.inventoryitem:IsHeld() then
        inst.components.deployhelper:StopHelper()
    end
end

---------------------------------------------------------------------------------------------------

local function DoOnDroppedLogic(inst)
    SwPigbotCommon.UpdateSpawnPoint(inst)

    inst.sg:GoToState(inst.components.fueled:IsEmpty() and "idle_broken" or "idle", true)
end

local function OnDropped(inst)
    -- 延迟一会儿,确认丢的地方是陆地还是水中或者是虚空?
    inst:DoTaskInTime(0, DoOnDroppedLogic)
end

local function OnPickup(inst, pickupguy, src_pos)
    inst.sg:GoToState("idle", true)

    if inst.brain ~= nil then
        inst.brain:UnignoreItem()
    end

    inst.components.fueled:StopConsuming()

    inst.components.locomotor:Stop()
    inst.components.locomotor:Clear()
    inst:ClearBufferedAction()

    inst.SoundEmitter:KillAllSounds()

    local item = inst.components.inventory:GetFirstItemInAnySlot() or inst.components.inventory:GetActiveItem() -- 这是故意反过来的,以便先给出较大的筹码.
    if item == nil then
        return
    end

    if pickupguy ~= nil and pickupguy.components.inventory ~= nil then
        if item ~= nil then
            pickupguy.components.inventory:GiveItem(item, nil, src_pos)
        end
    else
        if item ~= nil then
            inst.components.inventory:DropItem(item, true, true)
        end
    end
end

---------------------------------------------------------------------------------------------------

-- 损坏函数
local function SetBroken(inst)
    inst:AddTag("broken")

    RemovePhysicsColliders(inst)
end

local function OnBroken(inst)
    inst:SetBroken()

    if not inst.components.inventoryitem:IsHeld() and inst.sg.currentstate.name ~= "washed_ashore" then
        inst.sg:GoToState("breaking")
    end
end

local function OnRepaired(inst)
    inst:RemoveTag("broken")

    if inst.sg:HasStateTag("broken") then
        inst.sg:GoToState(inst.components.inventoryitem:IsHeld() and "idle" or "repairing_pre")
    end
end

---------------------------------------------------------------------------------------------------

local function OnSave(inst, data)
    data.NotNeedFuelToWork = inst.NotNeedFuelToWork
end

local function OnLoad(inst, data, newents)
    if data ~= nil and data.NotNeedFuelToWork ~= nil then
        inst.NotNeedFuelToWork = data.NotNeedFuelToWork
    end

    if inst.components.fueled:IsEmpty() then
        inst:SetBroken()

        inst.sg:GoToState("idle_broken")
    end

    SwPigbotCommon.UpdateSpawnPointOnLoad(inst)
end

---------------------------------------------------------------------------------------------------

local function DoOffscreenPickup(inst)
    if not inst:IsAsleep() then
        if inst._sleeptask ~= nil then
            inst._sleeptask:Cancel()
            inst._sleeptask = nil
        end

        return -- 保障.
    end

    local item = SwPigbotCommon.FindItemToPickupAndStore(inst)

    if item == nil then
        inst:StartOffscreenPickupTask(5)

        return
    end

    local container = SwPigbotCommon.FindContainerWithItem(inst, item)

    if container == nil then
        inst.components.inventory:DropItem(item, true, true)

        inst:StartOffscreenPickupTask(5)

        return
    end

    local dist = math.sqrt(distsq(container:GetPosition(), item:GetPosition()))

    -- 根据公式,物品到容器距离*2再除以移动速度,加上拾取物品放下物品的时间
    local time = (dist * 2 / inst.components.locomotor.walkspeed) + (59 + 57) * FRAMES -- Distance (container -> item and item -> container) / walkspeed + pickup and dropoff anim time.

    BufferedAction(inst, item, ACTIONS.PICKUP, nil, nil, nil, nil, nil, nil, inst.PICKUP_ARRIVE_DIST):Do()
    BufferedAction(inst, container, ACTIONS.STORE, item):Do()

    inst.components.inventory:CloseAllChestContainers()

    local fueled = inst.components.fueled

    fueled:DoDelta(-time * fueled.rate * fueled.rate_modifiers:Get())

    if not fueled:IsEmpty() then
        inst:StartOffscreenPickupTask(time)
    end
end

local function StartOffscreenPickupTask(inst, time)
    if inst._sleeptask ~= nil then
        inst._sleeptask:Cancel()
    end
    inst._sleeptask = inst:DoTaskInTime(time, inst.DoOffscreenPickup)
end

local function OnEntityWake(inst)
    -- 休眠任务关闭
    if inst._sleeptask ~= nil then
        inst._sleeptask:Cancel()
        inst._sleeptask = nil
    end
    -- 休眠传送任务关闭
    if inst._sleepteleporttask then
        inst._sleepteleporttask:Cancel()
        inst._sleepteleporttask = nil
    end
end

local function DoSleepTeleport(inst)
    inst._sleepteleporttask = nil

    if inst:IsInLimbo() or inst.components.fueled:IsEmpty() or inst.sg:HasAnyStateTag("drowning", "falling") then
        return
    end

    -- 传送到起始点
    inst.Physics:Teleport(SwPigbotCommon.GetSpawnPoint(inst):Get())

    -- 传送可能会唤醒.
    if not inst:IsAsleep() then
        return
    end

    -- 首先存放机器人自身物品栏中有的物品
    local item = inst.components.inventory:GetFirstItemInAnySlot() or inst.components.inventory:GetActiveItem() -- This is intentionally backwards to give the bigger stacks first.
    if item then
        local container = SwPigbotCommon.FindContainerWithItem(inst, item)
        if container then
            BufferedAction(inst, container, ACTIONS.STORE, item):Do()
            inst.components.inventory:CloseAllChestContainers()
        else
            inst.components.inventory:DropItem(item, true, true)
        end
    end

    -- 开始拾取其他地面物品,参数延迟多少时间
    inst:StartOffscreenPickupTask(1.5)
end

local function OnEntitySleep(inst)
    if inst:IsInLimbo() or inst.components.fueled:IsEmpty() or inst.sg:HasAnyStateTag("drowning", "falling") then
        return
    end

    inst.components.fueled:StopConsuming()
    inst.SoundEmitter:KillAllSounds()

    if inst.brain ~= nil then
        inst.brain:UnignoreItem()
    end

    -- 延迟传送,因为它可能会再次唤醒
    if inst._sleepteleporttask == nil then
        inst._sleepteleporttask = inst:DoTaskInTime(0, DoSleepTeleport)
    end
end

---------------------------------------------------------------------------------------------------

local function OnReachDestination(inst, data)
    if data.pos == nil or data.target == nil then
        return
    end

    -- 使其在正确图层
    if data.target.components.inventoryitem ~= nil and data.target.components.container == nil then
        local x, y, z = data.pos:Get()

        inst.Physics:Teleport(x, 0, z)
    end
end

---------------------------------------------------------------------------------------------------

local function OnTeleported(inst)
    SwPigbotCommon.UpdateSpawnPoint(inst)

    if inst._sleeptask ~= nil and not inst:IsAsleep() then
        inst._sleeptask:Cancel()
        inst._sleeptask = nil
    end
end

local function OnPercentUsedChange(inst)
    if inst:HasTag("broken") and (not inst.components.fueled:IsEmpty()) then
        OnRepaired(inst)
    end
end

---------------------------------------------------------------------------------------------------

local function GetStatus(inst, viewer)
    return inst.components.fueled:IsEmpty() and "BROKEN" or nil
end

---------------------------------------------------------------------------------------------------

local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    inst.entity:AddMiniMapEntity()
    inst.entity:AddDynamicShadow()
    inst.entity:AddNetwork()

    -- MakeGhostPhysics(inst, TUNING.STORAGE_ROBOT_MASS.FULL, 0.4) -- 添加物理属性,质量和碰撞半径
    MakeTinyFlyingCharacterPhysics(inst, TUNING.STORAGE_ROBOT_MASS.FULL, .5)

    inst.Transform:SetFourFaced()               -- 有四个面

    inst.MiniMapEntity:SetIcon("sw_pigbot.tex") -- 小地图图标 ※
    inst.MiniMapEntity:SetPriority(5)           -- 小地图图标显示优先级,没啥讲究的

    inst.DynamicShadow:SetSize(2.8, 1.7)        -- 设置动态阴影,可有可无没啥讲究的,保持和官方瓦器人一致

    inst:AddTag("companion")                    -- 伙伴标签,切斯特,格罗姆等也有这个标签
    inst:AddTag("NOBLOCK")                      -- 不影响行走和放置
    inst:AddTag("scarytoprey")                  -- 会惊吓小动物
    inst:AddTag("storagerobot")                 -- 专属标签:可储存机器人
    -- inst:AddTag("irreplaceable")                -- 珍贵标签,不可以带下线 ※
    inst:AddTag("donotautopick")                -- 不可以被老麦的暗影小人拾取
    inst:AddTag("showswinfo")

    MakeInventoryFloatable(inst, "med", 0.2)

    -- inst.SCANNABLE_RECIPENAME = "winona_storage_robot" -- 女工的扫描仪扫描之后学会的蓝图 ※

    inst._originx = net_float(inst.GUID, "sw_pigbot._originx", "origindirty") -- 原始X坐标
    inst._originz = net_float(inst.GUID, "sw_pigbot._originz", "origindirty") -- 原始Z坐标

    -- 动画资源指定和初始动画,不多解释 ※
    inst.AnimState:SetBank("sw_pigbot")
    inst.AnimState:SetBuild("sw_pigbot")
    inst.AnimState:PlayAnimation("idle", true)

    inst.Transform:SetScale(0.7, 0.7, 1)

    inst.AnimState:SetScale(VISUAL_SCALE, VISUAL_SCALE) -- 设置动画大小,是原动画的1.05倍

    -- inst.AnimState:Hide("light_on")                     -- 隐藏 light_on 图层,我们没有这个 ※

    -- 默认为0,当两个物品在同一个坐标动画重叠时,一个物品的 FinalOffset 值大于另一个物品;
    -- 优先让大的那个物品动画图层优先显示,也就是说猪机人在和箱子重叠时,箱子是被挡住的
    inst.AnimState:SetFinalOffset(1)

    -- 专用服务器不需要部署提示组件,只存在于玩家客户端.
    if not TheNet:IsDedicated() then
        inst:AddComponent("deployhelper")
        inst.components.deployhelper.onenablehelper = OnEnableHelper -- 需要显示／隐藏范围圈函数
        inst.components.deployhelper.onstarthelper = OnStartHelper   -- 一个回调函数,若被捡起,不会显示范围
    end

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        -- _originx和_originz发生变化,工作范围显示的那个圈也会发生变化
        inst:ListenForEvent("origindirty", OnOriginDirty)
        return inst
    end

    -- 翻译:拾取到达距离
    inst.PICKUP_ARRIVE_DIST       = 0
    inst.NotNeedFuelToWork        = false -- 不需要燃料也能工作

    inst.DoOffscreenPickup        = DoOffscreenPickup
    inst.StartOffscreenPickupTask = StartOffscreenPickupTask

    inst.OnReachDestination       = OnReachDestination -- 到达要捡起来的东西上方
    inst.SetBroken                = SetBroken          -- 损坏函数

    -- 添加一个各种坐标存储组件
    inst:AddComponent("knownlocations")

    -- 检查组件
    inst:AddComponent("inspectable")
    inst.components.inspectable.getstatus = GetStatus

    -- 物品栏组件,物品栏格子数为1
    inst:AddComponent("inventory")
    inst.components.inventory.maxslots = 1

    -- 物品组件
    inst:AddComponent("inventoryitem")
    -- 一个丢弃在地面的参数:意思是没有丢到地上的过程,一扔就立刻在丢弃的位置出现
    inst.components.inventoryitem.nobounce = true
    inst.components.inventoryitem:SetOnDroppedFn(OnDropped) -- 丢弃后执行函数
    inst.components.inventoryitem:SetOnPickupFn(OnPickup)   -- 捡起后执行函数

    -- 移动组件
    inst:AddComponent("locomotor")
    inst.components.locomotor:SetTriggersCreep(false)                         -- 无触发器
    inst.components.locomotor.pathcaps = { allowocean = true }                -- 可以在海上行走
    inst.components.locomotor.walkspeed = TUNING.STORAGE_ROBOT_WALKSPEED.FULL -- 移动速度

    -- 燃料组件
    inst:AddComponent("fueled")
    inst.components.fueled.fueltype = FUELTYPE.SW_PIGBOT
    inst.components.fueled:InitializeFuelLevel(9600)
    inst.components.fueled:SetDepletedFn(OnBroken)
    inst.components.fueled.accepting = true

    inst.getSwInfo = function(inst)
        if STRINGS.SW_STRINGS == nil then
            return ""
        end
        if STRINGS.SW_STRINGS.SW_PIGBOT_INFO == nil then
            return ""
        end
        local swstr = inst.NotNeedFuelToWork == true and STRINGS.SW_STRINGS.SW_PIGBOT_INFO.UPGRADED .. "\n" or STRINGS.SW_STRINGS.SW_PIGBOT_INFO.NONE .. "\n"
        swstr = swstr .. STRINGS.SW_STRINGS.SW_PIGBOT_INFO.FUEL .. math.floor(inst.components.fueled:GetPercent() * 100) .. "%"
        return swstr
    end

    -- inst:AddComponent("forgerepairable")
    -- inst.components.forgerepairable:SetRepairMaterial(FORGEMATERIALS.WAGPUNKBITS)
    -- inst.components.forgerepairable:SetOnRepaired(OnRepaired)

    -- 设置状态树和大脑
    inst:SetStateGraph("SGsw_pigbot")
    inst:SetBrain(brain)

    inst:ListenForEvent("onreachdestination", inst.OnReachDestination)

    inst:ListenForEvent("teleported", OnTeleported)

    inst:ListenForEvent("percentusedchange", OnPercentUsedChange)

    inst.OnSave = OnSave
    inst.OnLoad = OnLoad

    inst.OnEntitySleep = OnEntitySleep
    inst.OnEntityWake = OnEntityWake

    MakeHauntable(inst)

    return inst
end

return Prefab("sw_pigbot", fn, assets)
